今天要做的是透過 Opensea 取得 ticket 的圖片、擁有者等資訊,並將它們顯示在頁面中。
一般而言要從外部取得資料,是需要透過 API (Application Programming Interface) 來取得的。如果把 API 比喻成實體的物體,他就像一個連接應用程式之間的橋梁。就像是 ERC721 等協議,他們也屬於一種 API,只要是應用程式之間存在同一種協議,他們就可以透過 API 來取得各種相同格式的資料。
sited from astera.com
以下是我想要開發的頁面流程:
<Route>
將其變成一個分頁。useFetch()
我們自定義的函式以取得 ticket 的資訊。雖然這個步驟只有短短的幾步,但是花費了我好多好多的時間?。
首先這部非常簡單,就是在 src 中建立 Verify.js
,並與昨天一樣,新增一個包住 <Verify />
的 <Route>
並將其 path='/Verify'
。
另外我將原本左上角的選單(Menu)改成了三個 <Link/>
:
由左至右分別是 Home
、Mint
、Verify
三個頁面(其實在昨天的頁面中就可以看到了XD)。
useFetch()
取得資料在 React 中 fetch 別的地方的資料,需要使用 fetch
這個 function,但是直接使用 fetch 寫在 Verify.js
中似乎有點龐雜也不模組化,因此我沿用之前學習的資源,使用自定義的 Hook function,順便練習 useEffect()
的使用。
import { useState, useEffect } from "react";
const useFetch = (url) => {
const [data, setData] = useState(null);
const [isPending, setIsPending] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const abortCont = new AbortController();
setTimeout(() => {
fetch(url, { signal: abortCont.signal })
.then(res => {
if (!res.ok) { // error coming back from server
throw Error('could not fetch the data for that resource');
}
return res.json();
})
.then(data => {
setData(data);
setIsPending(false);
setError(null);
})
// send error
.catch(err => {
if (err.name === 'AbortError') {
console.log('fetch aborted');
}else {
setIsPending(false);
setError(err.message);
}
})
}, 4000);
return () => abortCont.abort();
}, [url]);
return { data, isPending, error };
}
export default useFetch;
這三個東西主要是拿來接住一些訊息的:
在 fetch
中,由於請求沒有辦法被取消,因此需要使用 abort()
來取消請求。
其邏輯是當 fetch 超過 setTimeout()
的時間時,會呼叫 abortController.abort()
再透過 signal = {signal}
傳送進去 fetch()
中取消請求。
而我們要從哪裡獲得 ticket 的資料呢?
Opensea 提供了開發者一些 end point,讓開發者可以透過 GET
的 HTTP method 來獲得來自 Opensea 的資料。
HTTP 是一個底層的網路協議,HTTP method 制定了資料的使用方式(例如查詢、修改、新增等),而在其中的
GET
就像是查詢存在於 url 中的資料。
從 Opensea 的 Doc 中可以找到 Retrieve an asset (Testnets) 的頁面,填入我們的合約地址並在 tokenId
輸入 3(因為目前只有 audience)。可以從旁邊的 try it! 可以找到可以使用的 api end point:
https://try.readme.io/https://testnets-api.opensea.io/api/v1/asset/0x4D2669542f0e6041C83c83cbEDa8df6262977Ec1/3/?account_address=0xaB50035F25F96dd3DEf1A7a9cC4E1C81AD6a7651
接下來就可以在 useFetch()
中傳入這個 url,便可以得到像這樣的資料:
{
id: 130756285,
num_sales: 0,
background_color: null,
image_url: 'https://lh3.googleusercontent.com/YPoAgmuHxay-uQyy…q7AJy0Jlw_yFUrljg3HnuJ2agMNJOit1qCuOHQFUYc5EOk85g',
image_preview_url: 'https://lh3.googleusercontent.com/YPoAgmuHxay-uQyy…0Jlw_yFUrljg3HnuJ2agMNJOit1qCuOHQFUYc5EOk85g=s250',
...more
}
事實上我們想要的資料就只有:top_ownerships
這個 key 裡面的 owner.address
(所有擁有這種票的地址) 和 quantity
(這個 owner 共有幾張票),還有 image_original_url
而已。
這邊使用 isPending
這個 boolean 來控制各種樣式的出現與否。
{
!isPending && userOwnsTickets &&
<div className="nft-box">
<img src={image} alt="image" className="nft-img"/>
<div className="nft-num">You have { ticketNum } tickets</div>
</div>
}
{ isPending && <div className="pending">Loading ...</div> }
{ !isPending && !userOwnsTickets && <div className="no-ticket"> You don't own any ticket yet! </div>}
useEffect(() => {
if (nftData !== null) {
const ownerships = nftData.top_ownerships;
for (const ownership of ownerships) {
if (accounts[0] === ownership.owner.address) {
setOwnsTicket(true);
setTicketNum(ownership.quantity)
setImage(nftData.image_original_url);
}
}
}
});
這邊單純的就是把剛剛提到我們需要的資訊用變數接住!
並最後在頁面中顯示:
<div className="nft-box">
<img src={image} alt="image" className="nft-img"/>
<div className="nft-num">You have { ticketNum } tickets</div>
</div>
這邊用了一個原本只有 4 張 ticket 的帳號, mint 了兩張 ticket,最後成功的 fetch 到了增加後的數量。
(可以發現我在測試的時候用了 Goerli ETH,主要是因為 Opensea 不支援在 Polygon 上 fetch 資料的原因,因此之後的開發鏈也會使用 Goerli 來測試)
這篇的內容看似簡單,但實際上花了我十幾個小時來實作。從我一開始想要使用 opensea-js 來獲得資料,發現 opensea-js 安裝在我的專案中會產生很多的 error,礙於時間問題而打消使用他的念頭,轉而使用 NFT-API 或 Moralis 等 api,但最後仍無功而返。最後終於找到一個非常非常簡單的 fetch 方式!
但是上述提到的內容我在未來也需要學會,希望可以早日查詢到那些 error 並用他們開發。(或是看看網路上影片中是如何實作的)
今天後會進入另一個階段:NFT other functions,主要是與 NFT 的相關的功能與變形!
若有文章內有任何錯誤的地方歡迎指點與討論!非常感謝!
歡迎贊助窮困潦倒大學生
0xd8538ea74825080c0c80B9B175f57e91Ff885Cb4